﻿#include "precompiled.h"
#include <io.h>
#include <fcntl.h>
#include <iostream>
#include <fstream>
#include "Util.h"

using namespace boost::numeric::ublas;

/*
 * Helper function to load gl extensions. Throws an exception
 * if the request process cannot be loaded.
 */
PROC Util::getGlProc(const char *procName) {
	PROC proc = wglGetProcAddress(procName);
	if(proc == NULL) {
		std::ostringstream msg;
		msg << __FILE__ << ":" << __LINE__ << " - ";
		msg << "Could not load function: " << procName;
		throw std::runtime_error(msg.str());
	}
	return proc;
}

/*
 * Reads an entire file into a char *. It is the
 * caller's responsibility to delete[] the returned pointer!
 */
char *Util::readEntireFile(const char *filename) {
	HANDLE hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, 0, NULL);
	if(hFile == INVALID_HANDLE_VALUE) {
		std::ostringstream msg;
		msg << "Failed to open file: " << filename;
		throw std::runtime_error(msg.str());
	}
	DWORD fileSize = GetFileSize(hFile, NULL);
	// allocate buffer
	char *buf = new char[fileSize + 1];
	DWORD bytesRead;
	BOOL success = ReadFile(hFile, buf, fileSize, &bytesRead, NULL);
	if(success == FALSE || bytesRead != fileSize) {
		CloseHandle(hFile);
		std::ostringstream msg;
		msg << "Error reading from file: " << filename;
		throw std::runtime_error(msg.str());
	}
	CloseHandle(hFile);
	buf[fileSize] = '\0';
	return buf;
}

void Util::verifyGlError() {
	verifyGlError(GL_NO_ERROR);
}

void Util::verifyGlError(GLenum expected) {
	GLenum error = glGetError();
	if(error != expected) {
		std::ostringstream msg;
		msg << "OpenGL error code: " << error;
		throw std::runtime_error(msg.str());
	}
}

ublas::matrix<double> Util::MakeRotate(double angle, double ux, double uy, double uz) {
	ublas::matrix<double> m(4, 4);

	double c = cos(angle);
	double s = sin(angle);

	m(0, 0) = ux * ux + (1 - ux * ux) * c;
	m(0, 1) = ux * uy * (1 - c) - uz * s;
	m(0, 2) = ux * uz * (1 - c) + uy * s;
	m(0, 3) = 0;
	m(1, 0) = ux * uy * (1 - c) + uz * s;
	m(1, 1) = uy * uy + (1 - uy * uy) * c;
	m(1, 2) = uy * uz * (1 - c) - ux * s;
	m(1, 3) = 0;
	m(2, 0) = ux * uz * (1 - c) - uy * s;
	m(2, 1) = uy * uz * (1 - c) + ux * s;
	m(2, 2) = uz * uz + (1 - uz * uz) * c;
	m(2, 3) = 0;
	m(3, 0) = 0;
	m(3, 1) = 0;
	m(3, 2) = 0;
	m(3, 3) = 1;

	return m;
}

void Util::MakeRotateInPlace(ublas::matrix<double> &m, double angle, double ux, double uy, double uz) {
	assert(m.size1() == 4 && m.size2() == 4);

	double c = cos(angle);
	double s = sin(angle);

	m(0, 0) = ux * ux + (1 - ux * ux) * c;
	m(0, 1) = ux * uy * (1 - c) - uz * s;
	m(0, 2) = ux * uz * (1 - c) + uy * s;
	m(0, 3) = 0;
	m(1, 0) = ux * uy * (1 - c) + uz * s;
	m(1, 1) = uy * uy + (1 - uy * uy) * c;
	m(1, 2) = uy * uz * (1 - c) - ux * s;
	m(1, 3) = 0;
	m(2, 0) = ux * uz * (1 - c) - uy * s;
	m(2, 1) = uy * uz * (1 - c) + ux * s;
	m(2, 2) = uz * uz + (1 - uz * uz) * c;
	m(2, 3) = 0;
	m(3, 0) = 0;
	m(3, 1) = 0;
	m(3, 2) = 0;
	m(3, 3) = 1.0;
}

void Util::LoadExtensions() {
	glBlendEquation = (PFNGLBLENDEQUATIONPROC)getGlProc("glBlendEquation");
}

// (a2b3 − a3b2, a3b1 − a1b3, a1b2 − a2b1)
ublas::vector<double> Util::cross_product(
	const ublas::vector<double> &a, const ublas::vector<double> &b)
{
	assert(a.size() == 3);
	assert(b.size() == 3);

	ublas::vector<double> c(3);
	c[0] = a[1] * b[2] - a[2] * b[1];
	c[1] = a[2] * b[0] - a[0] * b[2];
	c[2] = a[0] * b[1] - a[1] * b[0];
	return c;
}

// uses zxz convention
ublas::matrix<double> Util::MakeEulerMat(double h, double p, double r) {
	ublas::matrix<double> m(4, 4);
	
	double c1 = cos(h);
	double c2 = cos(p);
	double c3 = cos(r);

	double s1 = sin(h);
	double s2 = sin(p);
	double s3 = sin(r);

	m(0, 0) = c1*c3 - c2*s1*s3;
	m(0, 1) = -c3*s1 - c1*c2*s3;
	m(0, 2) = s2*s3;
	m(0, 3) = 0;
	
	m(1, 0) = c2*c3*s1 + c1*s3;
	m(1, 1) = c1*c2*c3 - s1*s3;
	m(1, 2) = -c3*s2;
	m(1, 3) = 0;
	
	m(2, 0) = s1*s2;
	m(2, 1) = c1*s2;
	m(2, 2) = c2;
	m(2, 3) = 0;
	
	m(3, 0) = 0;
	m(3, 1) = 0;
	m(3, 2) = 0;
	m(3, 3) = 1.0;

	return m;
}

// uses zxz convention
void Util::MakeEulerMatInPlace(matrix<double> &m, double h, double p, double r) {
	assert(m.size1() == 4 && m.size2() == 4);

	double c1 = cos(h);
	double c2 = cos(p);
	double c3 = cos(r);

	double s1 = sin(h);
	double s2 = sin(p);
	double s3 = sin(r);

	m(0, 0) = c1*c3 - c2*s1*s3;
	m(0, 1) = -c3*s1 - c1*c2*s3;
	m(0, 2) = s2*s3;
	m(0, 3) = 0;
	
	m(1, 0) = c2*c3*s1 + c1*s3;
	m(1, 1) = c1*c2*c3 - s1*s3;
	m(1, 2) = -c3*s2;
	m(1, 3) = 0;
	
	m(2, 0) = s1*s2;
	m(2, 1) = c1*s2;
	m(2, 2) = c2;
	m(2, 3) = 0;
	
	m(3, 0) = 0;
	m(3, 1) = 0;
	m(3, 2) = 0;
	m(3, 3) = 1.0;
}

ublas::matrix<double> Util::MakeTranslateMat(double tx, double ty, double tz) {
	ublas::matrix<double> m(4, 4);

	m(0, 0) = 1.0;
	m(0, 1) = 0;
	m(0, 2) = 0;
	m(0, 3) = tx;
	
	m(1, 0) = 0;
	m(1, 1) = 1.0;
	m(1, 2) = 0;
	m(1, 3) = ty;
	
	m(2, 0) = 0;
	m(2, 1) = 0;
	m(2, 2) = 1.0;
	m(2, 3) = tz;
	
	m(3, 0) = 0;
	m(3, 1) = 0;
	m(3, 2) = 0;
	m(3, 3) = 1.0;

	return m;
}

void Util::RedirectIOToConsole() {
	int hConHandle;
	long lStdHandle;
	CONSOLE_SCREEN_BUFFER_INFO coninfo;
	FILE *fp;
	// allocate a console for this app
	AllocConsole();
	// set the screen buffer to be big enough to let us scroll text
	GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &coninfo);
	coninfo.dwSize.Y = 500;
	SetConsoleScreenBufferSize(GetStdHandle(STD_OUTPUT_HANDLE), coninfo.dwSize);
	// redirect unbuffered STDOUT to the console
	lStdHandle = (long)GetStdHandle(STD_OUTPUT_HANDLE);
	hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
	fp = _fdopen( hConHandle, "w" );
	*stdout = *fp;
	setvbuf( stdout, NULL, _IONBF, 0 );
	// redirect unbuffered STDIN to the console
	lStdHandle = (long)GetStdHandle(STD_INPUT_HANDLE);
	hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
	fp = _fdopen( hConHandle, "r" );
	*stdin = *fp;
	setvbuf( stdin, NULL, _IONBF, 0 );
	// redirect unbuffered STDERR to the console
	lStdHandle = (long)GetStdHandle(STD_ERROR_HANDLE);
	hConHandle = _open_osfhandle(lStdHandle, _O_TEXT);
	fp = _fdopen( hConHandle, "w" );
	*stderr = *fp;
	setvbuf( stderr, NULL, _IONBF, 0 );
	// make cout, wcout, cin, wcin, wcerr, cerr, wclog and clog
	// point to console as well
	std::ios::sync_with_stdio();
}

void Util::assert_hr(HRESULT hr) {
	if(FAILED(hr)) {
		char buf[BUFSIZ];
		sprintf_s(buf, sizeof(buf), "HRESULT failed: 0x%x", hr);
		throw std::runtime_error(buf);
	}
}

/*
 * Source: http://acius2.blogspot.com/2007/11/calculating-next-power-of-2.html
 */
unsigned int Util::NextHighestPowerOf2(unsigned int val) {
	val--;
	val = (val >> 1) | val;
	val = (val >> 2) | val;
	val = (val >> 4) | val;
	val = (val >> 8) | val;
	val = (val >> 16) | val;
	val++; // Val is now the next highest power of 2.
	return val;
}

PFNGLBLENDEQUATIONPROC Util::glBlendEquation = 0;